package ar.edu.uba.fi.jmit.aspecter;

import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.List;

import javassist.ClassPool;
import javassist.CtClass;
import ar.edu.uba.fi.jmit.aspecter.core.interceptor.ConstructorAccessInterceptor;
import ar.edu.uba.fi.jmit.aspecter.core.interceptor.Interceptor;
import ar.edu.uba.fi.jmit.aspecter.monitor.testUtils.ConstructorDefinitionWithClassName;
import ar.edu.uba.fi.jmit.aspecter.testUtils.ExceptionBeforeConstructorInterceptor;


public class ConstructorAccessInterceptorTestCase extends AspecterTestCase {
	
	public void testInterceptSuperConstructor(){
		try {
			Interceptor interceptor = new ExceptionBeforeConstructorInterceptor("ar.edu.uba.fi.jmit.aspecter.testUtils.PointTest8");
			CtClass ctClass = ClassPool.getDefault().getAndRename("ar.edu.uba.fi.jmit.aspecter.testUtils.PointTest9", getNextClassName());
			ctClass.instrument(interceptor);
			Class<?> clazz = ctClass.toClass();
			try{
				clazz.newInstance();
				fail("exception showld be thrown!");
			}catch(RuntimeException e) {
				assertEquals("intercepted!", e.getMessage());
			}
			
			try{
				clazz.getConstructor(new Class<?>[]{int.class}).newInstance(2);
				fail("exception showld be thrown!");
			}catch(InvocationTargetException e) {
				//here should be wrapped the runtime exception
				assertEquals(RuntimeException.class, e.getTargetException().getClass());
				assertEquals("intercepted!", e.getTargetException().getMessage());
			}
			
		} catch (Exception e) {
			fail(e);
		}
	}
	
	
	public void testInterceptThisConstructor(){
		try {
			String className = getNextClassName();
			ConstructorDefinitionWithClassName definition = new ConstructorDefinitionWithClassName(
					int.class,
					className,
					null
			);
			Interceptor interceptor = new ExceptionBeforeConstructorInterceptor(definition);
			CtClass ctClass = ClassPool.getDefault().getAndRename("ar.edu.uba.fi.jmit.aspecter.testUtils.PointTest9", className);
			ctClass.instrument(interceptor);
			Class<?> clazz = ctClass.toClass();
			clazz.newInstance();
			try{
				clazz.getConstructor(new Class<?>[]{int.class}).newInstance(2);
				fail("exception showld be thrown!");
			}catch(InvocationTargetException e) {
				//here should be wrapped the runtime exception
				assertEquals(RuntimeException.class, e.getTargetException().getClass());
				assertEquals("intercepted!", e.getTargetException().getMessage());
			}
			clazz.getConstructor(new Class<?>[]{int.class, int.class}).newInstance(1, 2);
			try{
				clazz.getConstructor(new Class<?>[]{int.class, int.class, int.class}).newInstance(1, 2, 3);
				fail("exception showld be thrown!");
			}catch(InvocationTargetException e) {
				//here should be wrapped the runtime exception
				assertEquals(RuntimeException.class, e.getTargetException().getClass());
				assertEquals("intercepted!", e.getTargetException().getMessage());
			}
		} catch (Exception e) {
			fail(e);
		}
	}
	
	public void testInterceptThisConstructorWithParams(){
		try {
			String className = getNextClassName();
			List<Class<?>> params = new LinkedList<Class<?>>();
			params.add(int.class);
			params.add(int.class);
			ConstructorDefinitionWithClassName definition = new ConstructorDefinitionWithClassName(
					int.class,
					className,
					params
			);
			ConstructorAccessInterceptor interceptor = new ExceptionBeforeConstructorInterceptor(definition);
			interceptor.addConstructorToIntercept(definition);
			CtClass ctClass = ClassPool.getDefault().getAndRename("ar.edu.uba.fi.jmit.aspecter.testUtils.PointTest9", className);
			ctClass.instrument(interceptor);
			Class<?> clazz = ctClass.toClass();
			clazz.newInstance();
			clazz.getConstructor(new Class<?>[]{int.class}).newInstance(2);
			clazz.getConstructor(new Class<?>[]{int.class, int.class}).newInstance(1, 2);
			
			try{
				clazz.getConstructor(new Class<?>[]{int.class, int.class, int.class}).newInstance(1, 2, 3);
				fail("exception showld be thrown!");
			}catch(InvocationTargetException e) {
				//here should be wrapped the runtime exception
				assertEquals(RuntimeException.class, e.getTargetException().getClass());
				assertEquals("intercepted!", e.getTargetException().getMessage());
			}
			
		} catch (Exception e) {
			fail(e);
		}
	}

}
